import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
// import Sticky from 'components/StickyNode';
import Sticky from 'components/FixedTop/Sticky';
import FixedTop from 'components/FixedTop';
import cx from 'classnames';
import Touch from 'modules/Touch/core';
import WPT from 'wpt';
import styles from './index.less';

export default class Tabs extends PureComponent {
    static defaultProps = {
        className: '',
        slideShow: true,
        itemClassName: '',
        defaultSelected: undefined,
        onItemClick: undefined,
        queryName: 'tab',
        renderItem: undefined,
        sticky: false,
        stickyProps: {},
        mounting: false,
        fixed: false,
        scrollTop: true,
        sliderWidth: 1,
        theme: '', // 新增业务都应当使用符合最新规范的 wpt 主题
        selectedClassName: '',
        stickyCall: undefined,
        render: undefined,
        onItemClickCheck: undefined,
        usePageTouchX: false,
        useTabTouchX: false,
    };

    static propTypes = {
        className: PropTypes.string,
        slideShow: PropTypes.bool, // 显示底部横线
        itemClassName: PropTypes.string,
        items: PropTypes.arrayOf(
            PropTypes.shape({
                name: PropTypes.string.isRequired, // 显示名称
                style: PropTypes.object,
                className: PropTypes.string,
                onClick: PropTypes.func, // 点击事件
                key: PropTypes.any, // 对应 key 值
            })
        ).isRequired,
        defaultSelected: PropTypes.number, // 默认选中的index
        onItemClick: PropTypes.func, // tab 点击回调
        queryName: PropTypes.oneOfType([
            // query参数的名称
            PropTypes.string,
            PropTypes.bool,
        ]),
        renderItem: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), // 定制tab渲染
        sticky: PropTypes.bool, // 是否置顶吸附
        stickyProps: PropTypes.shape({
            activeClassName: PropTypes.string, // 显示名称
        }),
        mounting: PropTypes.bool, // 是否保持当前scrollTop不变，对于处在页面中间位置的Tabs，应当设置为true
        fixed: PropTypes.bool, // 是否使用fixed定位，对于处在页面顶部位置的Tabs应当设置为true
        scrollTop: PropTypes.bool, // 切换tab时，页面是否自动回到顶部 默认为true
        sliderWidth: PropTypes.number, // 下划线宽度
        theme: PropTypes.string,
        selectedClassName: PropTypes.string,
        stickyCall: PropTypes.func, // tab 点击回调
        render: PropTypes.func, // 定制tab渲染
        onItemClickCheck: PropTypes.func, // tab 点击回调
        usePageTouchX: PropTypes.bool, // 对于顶部是Tabs的页面，可以粗暴的监听整个页面的手势
        useTabTouchX: PropTypes.bool, // 监听Tabs props.render区域的元素
    };

    constructor(props) {
        super(props);

        const { defaultSelected, queryName, items } = this.props;
        const queryTabKey = WPT.Util.query()[queryName];
        let tabIndex;

        if (defaultSelected !== undefined) {
            tabIndex = defaultSelected;
        } else if (queryTabKey) {
            tabIndex = items.findIndex(item => item.key === queryTabKey);
        }

        if (tabIndex < 0 || tabIndex === undefined || tabIndex === 0) {
            tabIndex = 0;
        }

        this.state = {
            selectedIndex: tabIndex,
        };
    }

    componentDidMount() {
        this.addTouchX();
    }

    componentWillUnmount() {
        this.touchX && this.touchX.destroy();
    }

    stickyRef = React.createRef();

    initSlideBar = index => {
        const { selectedIndex } = this.state;
        const tabIndex = index >= 0 ? index : selectedIndex;

        this.setState({
            selectedIndex: tabIndex,
        });
    };

    handleItemClick = (item, index) => callback => {
        const {
            onItemClick, queryName, scrollTop, mounting, sticky
        } = this.props;

        let canChange = true;
        if (this.props.onItemClickCheck) {
            canChange = this.props.onItemClickCheck(item);
        }

        // 告知一下是change成功
        typeof callback === 'function' && callback(canChange);

        // 如果不可切换Tab下面的所有操作都不执行，很合理
        if (!canChange) return;

        // 滚动到顶部
        if (scrollTop) {
            $(window).scrollTop(0);
        }

        // 替换 url 里的参数, 如果 queryName 为空则不处理这个逻辑
        if (queryName && index !== this.state.selected) {
            WPT.replaceUrl(
                WPT.Util.urlAddQuery(`${window.location.pathname}${window.location.search}`, {
                    [queryName]: item.key || item.name,
                })
            );
        }

        this.setState({ selectedIndex: index }, () => {
            // 执行回调方法
            if (typeof onItemClick === 'function') {
                onItemClick(item);
            }

            if (typeof item.onClick === 'function') {
                item.onClick(item);
            }

            if (mounting && sticky) {
                this.stickyRef.current?.scrollToTop();
            }
        });
    };

    setTabRef = index => container => {
        this[`tab-${index}`] = container;
    };

    setSlideBarRef = container => {
        this.slideBar = container;

        if (this.slideBar) {
            this.initSlideBar();
        }
    };

    addTouchX = () => {
        const $slideRef = this.slideBar;
        if (!$slideRef) {
            return;
        }

        let $root = $($slideRef).parents('#Page')[0];
        if (!this.props.usePageTouchX) {
            [$root] = $($root).find('.tabTouchRootDom');
        }
        if (!$root) {
            return;
        }
        const { clientWidth } = $root;

        const relativeWidth = $slideRef ? $slideRef.clientWidth : this.props.sliderWidth;
        const scaleW = relativeWidth / clientWidth;
        const { items = [] } = this.props;
        let canSlide = true;

        this.touchX = new Touch($root, {
            preventDefault: false,
            activeClassName: 'Tab-touch-active',
            effectElements: [$slideRef],
            ingoreTouchElement: ['wpt-swipe', 'slick-slider'],
        })
            .on('start', () => {
                const { selectedIndex } = this.state;
                if (selectedIndex !== 0) {
                    $root.classList.add('wpt-swipe');
                }
                if (!$slideRef) {
                    return;
                }
                canSlide = true;
            })
            .on('change', ({ x }, state, e) => {
                const { selectedIndex } = this.state;
                if (
                    state.direction !== 'lr'
                    || !$slideRef
                    || (selectedIndex === 0 && x >= 0) // 为0时右滑不触发， class_active存在不触发
                    || (selectedIndex === items.length - 1 && x < 0) // 左滑到头为length-1时也不触发
                ) {
                    canSlide = false;
                    return;
                }
                canSlide = true;
                e.preventDefault();
                $slideRef.style.transform = `translateX(${relativeWidth * selectedIndex - x * scaleW}px)`;
            })
            .on('end', ({ x }, done) => {
                done();
                $root.classList.remove('wpt-swipe');
                if (!canSlide) {
                    return;
                }

                const { selectedIndex } = this.state;
                const reset = () => {
                    $slideRef.style.transform = `translateX(${relativeWidth * selectedIndex}px)`;
                };

                if (Math.abs(x) > 80) {
                    const index = x > 0 ? selectedIndex - 1 : selectedIndex + 1;

                    items[index]
                        && this.handleItemClick(
                            items[index],
                            index
                        )(success => {
                            // 如果没有变化成功，也重置回去
                            !success && reset();
                        });
                } else {
                    reset();
                }
            });
    };

    getItemClassName(item, index) {
        const { itemClassName, selectedClassName } = this.props;
        const { selectedIndex } = this.state;

        return cx(
            styles.item,
            itemClassName,
            item.className,
            selectedIndex === index && cx(styles.selected, selectedClassName)
        );
    }

    // 默认渲染 tab 方法
    defaultItemRenderer = (item, index) => (
        <div
            key={index}
            className={this.getItemClassName(item, index)}
            style={item.style || {}}
            onClick={this.handleItemClick(item, index)}
            ref={this.setTabRef(index)}
        >
            {item.name}
        </div>
    );

    // 渲染主体
    renderMain = () => {
        const {
            items, className, renderItem, slideShow, sliderWidth, theme
        } = this.props;
        const { selectedIndex } = this.state;

        const slideBarStyle = {
            width: `${100 / items.length}%`,
            transform: `translateX(${selectedIndex * 100}%)`,
            WebkitTransform: `translateX(${selectedIndex * 100}%)`,
        };
        const wrapCn = cx(styles.container, theme && styles[theme], className);
        return (
            <div
                ref={c => {
                    this.$root = c;
                }}
                className={wrapCn}
            >
                {items.length > 0
                    && items.map((i, k) => {
                        if (typeof renderItem === 'function') {
                            return renderItem(i, k, {
                                onClick: this.handleItemClick,
                                selected: selectedIndex === k,
                                className: this.getItemClassName(i, k),
                            });
                        }
                        return this.defaultItemRenderer(i, k);
                    })}
                {slideShow && (
                    <div className={styles.slideBar} style={slideBarStyle} ref={this.setSlideBarRef}>
                        <div className={styles.slideBarCore} style={{ width: `${100 * sliderWidth}%` }} />
                    </div>
                )}
            </div>
        );
    };

    handleStateChange = status => {
        // 2 sticky, 1 released status, 0 original
        const { stickyCall } = this.props;
        $.isFunction(stickyCall) && stickyCall(status.status);
    };

    renderHead() {
        const { sticky, stickyProps, fixed } = this.props;

        if (sticky) {
            return (
                <Sticky innerZ={999} onStateChange={this.handleStateChange} ref={this.stickyRef} {...stickyProps}>
                    {this.renderMain()}
                </Sticky>
            );
        }

        if (fixed) {
            return <FixedTop>{this.renderMain()}</FixedTop>;
        }

        return this.renderMain();
    }

    render() {
        const {
            sticky, items, mounting, useTabTouchX
        } = this.props;
        const { selectedIndex } = this.state;

        return (
            <>
                {this.renderHead()}
                {this.props.render && (
                    <div
                        style={{ minHeight: mounting && sticky ? '100vh' : 'auto' }}
                        className={cx(useTabTouchX && 'tabTouchRootDom')}
                    >
                        {this.props.render(items[selectedIndex])}
                    </div>
                )}
            </>
        );
    }
}
